Data Wrangling
Bevor wir mit den Visualisierungen und dem Erstellen der Modelle beginnen können, müssen wir die Daten säubern. Das heisst es sollte keine Duplikate geben, fehlende Werte sollten korrekt eingetragen werden und Daten, die nicht verwendet werden, sollten gelöscht werden.
Es hat “N/A” Werte in den Spalten “Year” und “Publisher”. Diese Werte sollten korrekte “NA” Werte sein, damit sie bei den Visualisierungen und Berechnungen nicht berücksichtigt werden.
# Show rows with "N/A" values
df[grep("N/A", df$Publisher),]
df[grep("N/A", df$Year),]
# Replace "N/A" with correct "NA" value
df[df == "N/A"] <- NA
# df <- df %>%
# filter(df$Global_Sales > 0.1)
#
# df
# Check if values have been converted
df %>%
summarize(across(everything(), ~sum(is.na(.))))
Da 2017 nur 3 Einträge und 2020 nur 1 Eintrag beinhaltet, werden wir diese Jahren nicht berücksichtigen und aus dem Dataframe löschen. Da diese nicht vollständig sind, könnten unsere Modelle ungenau werden.
# Remove years 2017 and 2020 from dataset
df_clean <- df[!(df$Year == "2017" | df$Year == "2020"),]
# Remove unnecessary columns because they are not needed for our thesis
drop <- c("Rank")
df_clean <- df_clean[,!(names(df_clean) %in% drop)]
df_clean
# Set data to correct type
df_clean$Genre <- as.factor(df_clean$Genre)
df_clean$Year <- as.numeric(df_clean$Year)
Datensatz verstehen
Wir erstellen ein paar erste Plots um uns einen Überblick über die Daten zu verschaffen.
na <- sum(df_clean[, 'NA_Sales'], na.rm = TRUE)
eu <- sum(df_clean[, 'EU_Sales'], na.rm = TRUE)
jp <- sum(df_clean[, 'JP_Sales'], na.rm = TRUE)
o <- sum(df_clean[, 'Other_Sales'], na.rm = TRUE)
g <- sum(df_clean[, 'Global_Sales'], na.rm = TRUE)
fig <- plot_ly(
y = c(na, eu, jp, o),
x = c("North America", "Europe", "Japan", "Other"),
type = 'bar',
width = 800
)
fig <- fig %>% layout(title = "Video Game Sales Overview",
xaxis = list(title = "Region"),
yaxis = list(title = "Sales (million)"))
fig
Anhand dieses Diagrammes, kann man sehen, dass Nordamerika mit Abstand der grösste Absatzmarkt im Game-Bereich ist.
Nun wollen wir noch die Verteilung der verschiedenen Spielgenres besser sehen:
# Group by genre and summarize game sales to each region
df_genre <- df_clean %>%
group_by(Genre) %>%
summarize(
NA_Sales_Sum = sum(NA_Sales),
EU_Sales_Sum = sum(EU_Sales),
JP_Sales_Sum = sum(JP_Sales),
Other_Sales_Sum = sum(Other_Sales),
Global_Sales_Sum = sum(Global_Sales)
)
# Plot grouped bar chart video game sales by genre
fig <- plot_ly(
df_genre, y = ~Genre, x = ~NA_Sales_Sum, type = "bar", name = "North America", width = 1000, height = 800) %>%
add_trace(x = ~EU_Sales_Sum, name = "Europe") %>%
add_trace(x = ~JP_Sales_Sum, name = "Japan") %>%
add_trace(x = ~Other_Sales_Sum, name = "Other") %>%
layout(
title = "Video Game Sales by Genre",
xaxis = list(title = "Sales (million)"),
barmode = "group"
)
fig
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Hier sieht man gut, dass Japan im Vergleich zu den anderen Märkten viel mehr Role-Playing und Strategie Spiele verkauft. Dafür werden in Japan aber viel weniger Shooter und Action Spiele als den anderen Regionen verkauft. EU und NA aber ähnliche Trends.
# Create Dataframe to show total sales for each year
df_year <- df_clean %>%
group_by(Year) %>%
summarize(
NA_Sales_Sum = sum(NA_Sales),
EU_Sales_Sum = sum(EU_Sales),
JP_Sales_Sum = sum(JP_Sales),
Other_Sales_Sum = sum(Other_Sales),
Global_Sales_Sum = sum(Global_Sales)
)
# Plot stacked barchart from the DataFrame above
fig <- plot_ly(
df_year, y = ~NA_Sales_Sum, x = ~Year, type = "bar", name = "North America", width = 900, height = 500) %>%
add_trace(y = ~EU_Sales_Sum, name = "Europe") %>%
add_trace(y = ~JP_Sales_Sum, name = "Japan") %>%
add_trace(y = ~Other_Sales_Sum, name = "Other") %>%
layout(
title = "Video Game from Sales by Year",
xaxis = list(title = "Year"),
yaxis = list(title = "Sales (million)"),
barmode = "stack"
)
fig
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
Überraschenderweise, sieht man in diesem Diagramm, dass die Videospiel Verkäufe zwischen den Jahren 2005 und 2012 geboomt haben. Danach ist die Kurve wieder abgeflacht. Dies könnte unter anderem daran liegen, dass in dieser Zeit viele neue Konsolen auf den Markt gekommen sind, welche grosse Neuerungen mit sich brachten, die nicht in diesem Datensatz sind (zb PS4). Die Playstation 3, die Xbox 360 und die Nintendo Wii sind alle in 2005-2006 auf den Markt gekommen und haben den Spielemarkt anscheinend stark beeinflusst.
# Create Dataframe and calculate the average of sales for each genre from each region
df_sales_avg <- df_clean %>%
group_by(Genre) %>%
summarise(
EU_Sales_Avg = mean(EU_Sales),
NA_Sales_Avg = mean(NA_Sales),
JP_Sales_Avg = mean(JP_Sales),
Other_Sales_Avg = mean(Other_Sales),
Global_Sales_Avg = mean(Global_Sales))
df_sales_avg
Hier sehen wir die durchschnittliche Anzhal von Verkäufe pro Genre.
Regressionsmodelle
Da wir unsere Daten jetzt besser verstehen, können wir mit den Regressionsmodellen und mit der Beantwortung unserer Fragestellung beginnen. Allerdings mussten wir feststellen, dass unsere Fragestellung ungeschickt war und wir diese nicht ausreichend beantworten können. Daher haben wir uns folgende Alternativ-Fragestellung überlegt:
Kann man anhand der nordamerikanischen Verkäufe voraussagen, wie sich ein Genre im europäischen Markt verkaufen wird?
Diese Frage könnte einer Firma dabei helfen, zu entscheiden, wieviel Werbebudget diese in Europa ausgeben soll, nachdem ein Spiel in Amerika bereits auf den Markt gekommen ist.
Daten optimieren
In unserem Datensatz gibt es einige Ausreisser. Zum Beispiel gibt es Spiele, welche nur in einzelnen Regionen auf den Markt gekommen sind und die in anderen Ländern gar nicht verkauft wurden. Diese Tatsache würde beim Erstellen der Modelle zu Abweichungen und Ungenauigkeiten führen. Daher berücksichtigen wir nur die Games, welche über 1 Mio. Verkäufe haben.
Unten haben wir die Genres mit den besten Fits genommen, da bei den anderen Spielgenres der R-Squared Wert unter 50% lag. Das bedeutet, dass es dort keinen Zusammenhang gibt, weshalb wir diese ebenfalls nicht beachten werden.
# Create DataFrame only with Racing games
df_racing <- df_clean %>%
filter(
Genre == "Racing",
NA_Sales > 1.00,
EU_Sales > 1.00
)
#create scatterplot
ggplot(df_racing, aes(x=EU_Sales, y=NA_Sales)) +
geom_point() +
geom_smooth(method = "lm", se=FALSE) +
labs(title = "Racing Game Sales Overview")
`geom_smooth()` using formula 'y ~ x'

# Create linear model
mdl_racing <- lm(EU_Sales ~ NA_Sales, data = df_racing)
# Extract model score
mdl_racing %>%
glance() %>%
pull(r.squared)
[1] 0.8686435
R.Squared ist sozusagen die Genauigkeit des Modells. Dieses Modell von Racing Games in der EU und in NA liegt bei einer akzeptablen Wert von 86%
# Predict EU Sales for a Racing Game based on NA Sales
predict_racing <- tibble(NA_Sales = 5)
predict(mdl_racing, predict_racing)
1
3.891789
Vorhersage
Wenn ein Racing Game in Nord Amerika 5 Millionen Verkäufe aufweist, liegt die Verkaufs-Vorhersage für Europa bei rund 3.9 Millionen. In Amerika sind diese Spiele also beliebter als bei uns in Europa.
# Create DataFrame only with Role-Playing games
df_role <- df_clean %>%
filter(
Genre == "Role-Playing",
NA_Sales > 1.00,
EU_Sales > 1.00
)
#create scatterplot
ggplot(df_role, aes(x=EU_Sales, y=NA_Sales)) +
geom_point() +
geom_smooth(method = "lm", se=FALSE) +
labs(title = "Role-Playing Game Sales Overview")
`geom_smooth()` using formula 'y ~ x'

# Create linear model
mdl_rp <- lm(EU_Sales ~ NA_Sales, data = df_role)
# Extract model score
mdl_rp %>%
glance() %>%
pull(r.squared)
[1] 0.8433709
Das Modell von Role-Playing Spiele in NA und EU beträgt auch ein akzeptabler Wert von 84%
# Predict EU Sales for a Role-Playing Game based on NA Sales
predict_rp <- tibble(NA_Sales = 5)
predict(mdl_rp, predict_rp)
1
3.451286
# Create DataFrame only with Simulation games
df_sim <- df_clean %>%
filter(
Genre == "Simulation",
NA_Sales > 1.00,
EU_Sales > 1.00
)
# Create scatterplot
ggplot(df_sim, aes(x=EU_Sales, y=NA_Sales)) +
geom_point() +
geom_smooth(method = "lm", se=FALSE) +
labs(title = "Simulation Game Sales Overview")
`geom_smooth()` using formula 'y ~ x'

# Create linear model
mdl_sim <- lm(EU_Sales ~ NA_Sales, data = df_sim)
# Extract model score
mdl_sim %>%
glance() %>%
pull(r.squared)
[1] 0.9233418
Das Modell von Simulation Spiele in NA und EU beträgt auch ein genauer Wert von 92%
# Predict EU Sales for a Sport Game based on NA Sales
predict_sim <- tibble(NA_Sales = 5)
predict(mdl_sim, predict_sim)
Vorhersage
Wenn ein Simulation Game in NA 5 Million Verkäufe aufweist, liegt die Vorhersage für EU bei 5.4 Millionen. Und Somit ist die Genre Simulation die erste, bei der die Vorhersagen der Verkäufe grösser sind als in Nord Amerika.
# Create DataFrame only with Sport games
df_sport <- df_clean %>%
filter(
Genre == "Sports",
NA_Sales > 1.00,
EU_Sales > 1.00
)
ggplot(df_sport, aes(x=EU_Sales, y=NA_Sales)) +
geom_point() +
geom_smooth(method = "lm", se=FALSE) +
labs(title = "Sport Game Sales Overview")
`geom_smooth()` using formula 'y ~ x'

Hier haben wir wieder ein Spiel, welches sich von den Massen abhebt, aber in Amerika sowie in Europa extrem beliebt war. Es handelt sich natürlich um das Kultspiel “Wii Sports”. Dieses wurde einem auch meistens beim Kauf einer Nintendo Wii Konsole dazu geschenkt.
# Create linear model
mdl_sport <- lm(EU_Sales ~ NA_Sales, data = df_sport)
# Extract model score
mdl_sport %>%
glance() %>%
pull(r.squared)
[1] 0.9488926
Das Modell von Sport Spiele in NA und EU ist sehr genau bei knapp 95%. Dadurch sind die Vorhersagen ebenfalls sehr genau.
# Predict EU Sales for a Sport Game based on NA Sales
predict_sport <- tibble(NA_Sales = 5)
predict(mdl_sport, predict_sport)
1
4.019965
Vorhersage
Wenn ein Sport Game in NA 5 Million Verkäufe aufweist, liegt die Vorhersage für EU bei 4 Millionen.
Residuenanalyse (zum beurteilen ob das Modell gut ist)
Die Residuen sollten folgende Punkte erfüllen:
- Residuen haben den erwartungswert von 0
- Residuen sind voneinander unabhängig
- Residuen sind normalverteilt
Dies müssen wir nun noch prüfen, um zu bestimmen, ob unsere Vorhersagen verlässlich sind.
# Create Residual Scatterplot
df <- augment(mdl_racing)
# plot residuals
ggplot(df, aes(x = 1:nrow(df), y = .resid)) +
geom_point() +
geom_hline(yintercept=0, color="Red") +
ggtitle("Residuals Model Racing Genre") +
xlab("")

# Create Residual Histogram (to see if the data is a normal distribution)
ggplot(df_racing, aes(x = mdl_racing$residuals)) +
geom_histogram(bins = 30) +
geom_density(color = "Red") +
ggtitle("Residuals Model Racing Genre") +
xlab("residuals")

Mit Hilfe der roten Linie sieht man, dass das Histogramm nicht 100% normalverteilt ist. Es kommt jedoch schon nahe an eie Normalverteilung heran, weshalb wir das so akzeptieren können.
# Create Residual Scatterplot
df <- augment(mdl_rp)
ggplot(df, aes(x = 1:nrow(df), y = .resid)) +
geom_point() +
geom_hline(yintercept=0, color="Red") +
ggtitle("Residuals Model Role-Playing Genre") +
xlab("")

# Create Residual Histogram (to see if the data is a normal distribution)
ggplot(df_role, aes(x = mdl_rp$residuals)) +
geom_histogram(bins = 30) +
geom_density(color = "Red") +
ggtitle("Residuals Model Role-Playing Genre") +
xlab("residuals")

Auch sieht man, dass das Histogramm nicht 100% normalverteilt ist. Es kommt jedoch schon nahe an eie Normalverteilung heran, weshalb wir das so akzeptieren können.
# Create Residual Scatterplot
df <- augment(mdl_sim)
ggplot(df, aes(x = 1:nrow(df), y = .resid)) +
geom_point() +
geom_hline(yintercept=0, color="Red") +
ggtitle("Residuals Model Simulation Genre") +
xlab("")

# Create Residual Histogram (to see if the data is a normal distribution)
ggplot(df_sim, aes(x = mdl_sim$residuals)) +
geom_histogram(bins = 30) +
geom_density(color = "Red") +
ggtitle("Residuals Model Simulation Genre") +
xlab("residuals")

Mit Hilfe der roten Linie sieht man, dass das Histogramm nicht 100% normalverteilt ist. Es kommt jedoch schon nahe an eie Normalverteilung heran, weshalb wir das so akzeptieren können.
# Create Residual Scatterplot
df <- augment(mdl_sport)
ggplot(df, aes(x = 1:nrow(df), y = .resid)) +
geom_point() +
geom_hline(yintercept=0, color="Red") +
ggtitle("Residuals Model Sport Genre") +
xlab("")

# Create Residual Histogram (to see if the data is a normal distribution)
ggplot(df_sport, aes(x = mdl_sport$residuals)) +
geom_histogram(bins = 50) +
geom_density(color = "Red") +
ggtitle("Residuals Model Sport Genre") +
xlab("residuals")

Mit Hilfe der roten Linie sieht man, dass das Histogramm nicht 100% normalverteilt ist. Es kommt jedoch schon nahe an eie Normalverteilung heran, weshalb wir das so akzeptieren können.
Wie ähnlich sind sich die Verkaufszahlen von Nordamerika und Europa? Und wie verhalten sich die japanischen Verkaufszahlen im Vergleich zu den zwei westlichen Absatzmärkten?
# Color the SPLOM of NA_Sales, EU_Sales, and JP_Sales by nintendo
df_clean %>%
plot_ly(color = ~Genre) %>%
add_trace(
type = 'splom',
dimensions = list(
list(label = 'N. America', values = ~NA_Sales),
list(label = 'Europe', values = ~EU_Sales),
list(label = 'Japan', values = ~JP_Sales)
)
)
Warning in RColorBrewer::brewer.pal(N, "Set2") :
n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
Warning in RColorBrewer::brewer.pal(N, "Set2") :
n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
Warning in RColorBrewer::brewer.pal(N, "Set2") :
n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
Warning in RColorBrewer::brewer.pal(N, "Set2") :
n too large, allowed maximum for palette Set2 is 8
Returning the palette you asked for with that many colors
Äusserst spannend zu sehen ist, dass sich grosse Unterschiede sehen lassen, wie sich bestimmte Genre verkauft haben. Die geographische Lage von dem Absatzmarkt ist dabei eher zweitrangig. Mit Abstand am häufigsten wurden Action Spiele verkauft.
Verkaufen sich Nintendo Spiele weltweit besser als Spiele Electronic Arts.
Dafür müssen wir zuerst unser Dataframe Filtern, damit wir nurnoch Spiele von Nintendo und Electronic Arts haben.
# Create DataFrame with only Nintendo and Sony as Publisher
publishers = c("Nintendo", "Electronic Arts")
df_publisher <- df_clean %>%
filter(
Publisher %in% publishers
)
df_publisher
# Replace publisher name with 0 and 1
df_publisher$Publisher[df_publisher$Publisher == "Electronic Arts"] <- 0
df_publisher$Publisher[df_publisher$Publisher == "Nintendo"] <- 1
# Save as int
df_publisher$Publisher <- as.numeric(df_publisher$Publisher)
# Create logistic model
mdl_publisher <- glm(Publisher ~ NA_Sales, data = df_publisher, family = binomial())
ggplot(df_publisher, aes(x=Global_Sales, y=Publisher)) +
geom_point(alpha=.5, color="Blue") +
stat_smooth(method="glm", col = "Red", se=FALSE, method.args = list(family=binomial)) +
labs(
x = "Sales (million)",
y = "1=Nintendo / 0=Electronic Arts",
title = "Probability that a game is from Nintendo based on global sales"
)
`geom_smooth()` using formula 'y ~ x'

Wir können hier gut sehen, dass Nintendo viel erfolgreichere Spiele produziert hat. Dies deutet auch an, dass Nintendo beliebter ist als Electronic Arts.
Verkaufen sich in Japan Nintendo Wii Spiele besser als Nintendo DS Spiele?
Nun filtern wir zuerst unseren Dataframe nach Wii und nach DS Spielen.
# Create DataFrame with only Wii and DS as Platforms
platforms = c("Wii", "DS")
df_platform <- df_clean %>%
filter(
Platform %in% platforms
)
df_platform
# Replace platform name with 0 and 1
df_platform$Platform[df_platform$Platform == "Wii"] <- 0
df_platform$Platform[df_platform$Platform == "DS"] <- 1
# Save as int
df_platform$Platform <- as.numeric(df_platform$Platform)
# Create logistic model
mdl_platform <- glm(Platform ~ JP_Sales, data = df_platform, family = binomial())
ggplot(df_platform, aes(x=JP_Sales, y=Platform)) +
geom_point(alpha=.2, color="Blue") +
stat_smooth(method="glm", col = "Red", se=FALSE, method.args = list(family=binomial)) +
labs(
x = "Sales (million)",
y = "1=Wii / 0=DS",
title = "Probability that a game is from Wii based on it's sales in Japan"
)
`geom_smooth()` using formula 'y ~ x'

Anhand von diesem Plot kann man erkennen, dass ein Spiel, welches sich über 2 Mio. mal verkauft hat, eher über die Platform Wii verkauft wurde. Daraus könnte man deuten, dass die Nintendo Wii in Japan beliebter ist als der Nintendo DS.
Fazit
Bei den meisten Genren ist es nicht möglich die Verkäufe in Europa anhand der Verkäufe in Nord Amerika vorherzusagen. Wir haben jedoch einige Genren gefunden, bei denen Vorhersagen möglich ist:
- Racing / Rennspiele
- Role-Playing / Rollenspiele
- Simulation / Simulationsspiele
- Sport / Sportspiele
Sehr interessant zu sehen war das Genre Simulation. Unser Modell, welches bei 92% Genauigkeit liegt sagt voraus, dass ein beliebiges Simulation Spiel in Europa besser verkauft wird als in Nord Amerika. Bei allen anderen Genren verkaufen sich die Spiele in Nord Amerika besser.
LS0tDQp0aXRsZTogIlJlZ3Jlc3Npb24gbW9kZWxzIHdpdGggUiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCkRhcyBaaWVsIGlzdCBlcywgYXVzIGRlbSBEYXRhY2FtcCBEYXRlbnNhdHogW1ZpZGVvIEdhbWUgU2FsZXMgRGF0ZV0oaHR0cHM6Ly9hcHAuZGF0YWNhbXAuY29tL3dvcmtzcGFjZS9kYXRhc2V0cy9kYXRhc2V0LXB5dGhvbi12aWRlby1nYW1lcy1zYWxlcykgZm9sZ2VuZGUgRnJhZ2VzdGVsbHVuZyAvIEh5cG90aGVzZSB6dSBiZWFudHdvcnRlbjoNCg0KDQojIyMgRnJhZ2VzdGVsbHVuZzogIklzdCBlcyB3YWhyc2NoZWlubGljaGVyLCBkYXNzIHNpY2ggYmVzdGltbXRlIFNwaWVsZ2VucmVzIGluIEV1cm9wYSBzaWduaWZpa2FudCAoVW50ZXJzY2hpZWQgdm9uIDUwJSkgYmVzc2VyIHZlcmthdWZlbiBsYXNzZW4gYWxzIGltIEphcGFuaXNjaGVuIHVuZCBOb3JkYW1lcmlrYW5pc2NoZW4gTWFya3Q/Ig0KDQoNCkFscyBFaW5mw7xocnVuZyB3ZXJkZW4gd2lyIGF1ZiBEYXRhY2FtcCBmb2xnZW5kZXIgS3VycyBkdXJjaGdlaGVuOg0KDQotIFtJbnRyb2R1Y3Rpb24gdG8gUmVncmVzc2lvbiBpbiBSXShodHRwczovL2FwcC5kYXRhY2FtcC5jb20vbGVhcm4vY291cnNlcy9pbnRyb2R1Y3Rpb24tdG8tcmVncmVzc2lvbi1pbi1yKQ0KDQpGYWxscyB3aXIgbm9jaCB3ZWl0ZXJlIEluZm9ybWF0aW9uZW4gw7xiZXIgUmVncmVzc2lvbnNtb2RlbGxlIGJyYXVjaGVuLCBzdGVodCB1bnMgZGllc2VyIEt1cnMgenVyIFZlcmbDvGd1bmc6DQoNCi0gW0ludGVybWVkaWF0ZSBSZWdyZXNzaW9uIGluIFJdKGh0dHBzOi8vYXBwLmRhdGFjYW1wLmNvbS9sZWFybi9jb3Vyc2VzL2ludGVybWVkaWF0ZS1yZWdyZXNzaW9uLWluLXIpDQoNCg0KYGBge3J9DQojIEltcG9ydCBsaWJyYXJpZXMNCmxpYnJhcnkoInBsb3RseSIpDQpsaWJyYXJ5KCJnZ3Bsb3QyIikNCmxpYnJhcnkoInBseXIiKQ0KbGlicmFyeSgiZHBseXIiKQ0KbGlicmFyeSgiYnJvb20iKQ0KbGlicmFyeSgiZ3JpZEV4dHJhIikgICANCmBgYA0KDQpgYGB7cn0NCiMgUmVhZCBjc3YgZnJvbSBmb2xkZXIgImRhdGEiDQpkZiA9IHJlYWQuY3N2KCIuL2RhdGEvdmlkZW9fZ2FtZXNfZGF0YS5jc3YiKQ0KDQpoZWFkKGRmLCAxMCkNCmBgYA0KIyMjIERhdGEgV3JhbmdsaW5nDQpCZXZvciB3aXIgbWl0IGRlbiBWaXN1YWxpc2llcnVuZ2VuIHVuZCBkZW0gRXJzdGVsbGVuIGRlciBNb2RlbGxlIGJlZ2lubmVuIGvDtm5uZW4sIG3DvHNzZW4gd2lyIGRpZSBEYXRlbiBzw6R1YmVybi4gRGFzIGhlaXNzdCBlcyBzb2xsdGUga2VpbmUgRHVwbGlrYXRlIGdlYmVuLCBmZWhsZW5kZSBXZXJ0ZSBzb2xsdGVuIGtvcnJla3QgZWluZ2V0cmFnZW4gd2VyZGVuIHVuZCBEYXRlbiwgZGllIG5pY2h0IHZlcndlbmRldCB3ZXJkZW4sIHNvbGx0ZW4gZ2Vsw7ZzY2h0IHdlcmRlbi4NCg0KRXMgaGF0ICJOL0EiIFdlcnRlIGluIGRlbiBTcGFsdGVuICJZZWFyIiB1bmQgIlB1Ymxpc2hlciIuIERpZXNlIFdlcnRlIHNvbGx0ZW4ga29ycmVrdGUgIk5BIiBXZXJ0ZSBzZWluLCBkYW1pdCBzaWUgYmVpIGRlbiBWaXN1YWxpc2llcnVuZ2VuIHVuZCBCZXJlY2hudW5nZW4gbmljaHQgYmVyw7xja3NpY2h0aWd0IHdlcmRlbi4NCg0KYGBge3J9DQojIFNob3cgcm93cyB3aXRoICJOL0EiIHZhbHVlcw0KZGZbZ3JlcCgiTi9BIiwgZGYkUHVibGlzaGVyKSxdDQpkZltncmVwKCJOL0EiLCBkZiRZZWFyKSxdDQpgYGANCg0KYGBge3J9DQojIFJlcGxhY2UgIk4vQSIgd2l0aCBjb3JyZWN0ICJOQSIgdmFsdWUNCmRmW2RmID09ICJOL0EiXSA8LSBOQQ0KDQojIGRmIDwtIGRmICU+JQ0KIyAgIGZpbHRlcihkZiRHbG9iYWxfU2FsZXMgPiAwLjEpDQojIA0KIyBkZg0KYGBgDQoNCmBgYHtyfQ0KIyBDaGVjayBpZiB2YWx1ZXMgaGF2ZSBiZWVuIGNvbnZlcnRlZA0KZGYgJT4lIA0KICBzdW1tYXJpemUoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgfnN1bShpcy5uYSguKSkpKQ0KYGBgDQpEYSAyMDE3IG51ciAzIEVpbnRyw6RnZSB1bmQgMjAyMCBudXIgMSBFaW50cmFnIGJlaW5oYWx0ZXQsIHdlcmRlbiB3aXIgZGllc2UgSmFocmVuIG5pY2h0IGJlcsO8Y2tzaWNodGlnZW4gdW5kIGF1cyBkZW0gRGF0YWZyYW1lIGzDtnNjaGVuLiBEYSBkaWVzZSBuaWNodCB2b2xsc3TDpG5kaWcgc2luZCwga8O2bm50ZW4gdW5zZXJlIE1vZGVsbGUgdW5nZW5hdSB3ZXJkZW4uDQoNCmBgYHtyfQ0KIyBSZW1vdmUgeWVhcnMgMjAxNyBhbmQgMjAyMCBmcm9tIGRhdGFzZXQNCmRmX2NsZWFuIDwtIGRmWyEoZGYkWWVhciA9PSAiMjAxNyIgfCBkZiRZZWFyID09ICIyMDIwIiksXQ0KDQojIFJlbW92ZSB1bm5lY2Vzc2FyeSBjb2x1bW5zIGJlY2F1c2UgdGhleSBhcmUgbm90IG5lZWRlZCBmb3Igb3VyIHRoZXNpcw0KZHJvcCA8LSBjKCJSYW5rIikNCmRmX2NsZWFuIDwtIGRmX2NsZWFuWywhKG5hbWVzKGRmX2NsZWFuKSAlaW4lIGRyb3ApXQ0KDQpkZl9jbGVhbg0KYGBgDQpgYGB7cn0NCiMgU2V0IGRhdGEgdG8gY29ycmVjdCB0eXBlDQpkZl9jbGVhbiRHZW5yZSA8LSBhcy5mYWN0b3IoZGZfY2xlYW4kR2VucmUpDQpkZl9jbGVhbiRZZWFyIDwtIGFzLm51bWVyaWMoZGZfY2xlYW4kWWVhcikNCmBgYA0KDQojIyMgRGF0ZW5zYXR6IHZlcnN0ZWhlbg0KDQpXaXIgZXJzdGVsbGVuIGVpbiBwYWFyIGVyc3RlIFBsb3RzIHVtIHVucyBlaW5lbiDDnGJlcmJsaWNrIMO8YmVyIGRpZSBEYXRlbiB6dSB2ZXJzY2hhZmZlbi4NCg0KYGBge3J9DQpuYSA8LSBzdW0oZGZfY2xlYW5bLCAnTkFfU2FsZXMnXSwgbmEucm0gPSBUUlVFKQ0KZXUgPC0gc3VtKGRmX2NsZWFuWywgJ0VVX1NhbGVzJ10sIG5hLnJtID0gVFJVRSkNCmpwIDwtIHN1bShkZl9jbGVhblssICdKUF9TYWxlcyddLCBuYS5ybSA9IFRSVUUpDQpvIDwtIHN1bShkZl9jbGVhblssICdPdGhlcl9TYWxlcyddLCBuYS5ybSA9IFRSVUUpDQpnIDwtIHN1bShkZl9jbGVhblssICdHbG9iYWxfU2FsZXMnXSwgbmEucm0gPSBUUlVFKQ0KDQpmaWcgPC0gcGxvdF9seSgNCiAgeSA9IGMobmEsIGV1LCBqcCwgbyksIA0KICB4ID0gYygiTm9ydGggQW1lcmljYSIsICJFdXJvcGUiLCAiSmFwYW4iLCAiT3RoZXIiKSwgDQogIHR5cGUgPSAnYmFyJywNCiAgd2lkdGggPSA4MDANCikNCg0KZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gIlZpZGVvIEdhbWUgU2FsZXMgT3ZlcnZpZXciLA0KICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIlJlZ2lvbiIpLA0KICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlNhbGVzIChtaWxsaW9uKSIpKQ0KDQpmaWcNCmBgYA0KQW5oYW5kIGRpZXNlcyBEaWFncmFtbWVzLCBrYW5uIG1hbiBzZWhlbiwgZGFzcyBOb3JkYW1lcmlrYSBtaXQgQWJzdGFuZCBkZXIgZ3LDtnNzdGUgQWJzYXR6bWFya3QgaW0gR2FtZS1CZXJlaWNoIGlzdC4gDQoNCg0KTnVuIHdvbGxlbiB3aXIgbm9jaCBkaWUgVmVydGVpbHVuZyBkZXIgdmVyc2NoaWVkZW5lbiBTcGllbGdlbnJlcyBiZXNzZXIgc2VoZW46DQoNCmBgYHtyfQ0KIyBHcm91cCBieSBnZW5yZSBhbmQgc3VtbWFyaXplIGdhbWUgc2FsZXMgdG8gZWFjaCByZWdpb24NCmRmX2dlbnJlIDwtIGRmX2NsZWFuICU+JQ0KICBncm91cF9ieShHZW5yZSkgJT4lDQogIHN1bW1hcml6ZSgNCiAgICBOQV9TYWxlc19TdW0gPSBzdW0oTkFfU2FsZXMpLA0KICAgIEVVX1NhbGVzX1N1bSA9IHN1bShFVV9TYWxlcyksIA0KICAgIEpQX1NhbGVzX1N1bSA9IHN1bShKUF9TYWxlcyksDQogICAgT3RoZXJfU2FsZXNfU3VtID0gc3VtKE90aGVyX1NhbGVzKSwNCiAgICBHbG9iYWxfU2FsZXNfU3VtID0gc3VtKEdsb2JhbF9TYWxlcykNCiAgKQ0KDQojIFBsb3QgZ3JvdXBlZCBiYXIgY2hhcnQgdmlkZW8gZ2FtZSBzYWxlcyBieSBnZW5yZQ0KZmlnIDwtIHBsb3RfbHkoDQogIGRmX2dlbnJlLCB5ID0gfkdlbnJlLCB4ID0gfk5BX1NhbGVzX1N1bSwgdHlwZSA9ICJiYXIiLCBuYW1lID0gIk5vcnRoIEFtZXJpY2EiLCB3aWR0aCA9IDEwMDAsIGhlaWdodCA9IDgwMCkgJT4lIA0KICBhZGRfdHJhY2UoeCA9IH5FVV9TYWxlc19TdW0sIG5hbWUgPSAiRXVyb3BlIikgJT4lDQogIGFkZF90cmFjZSh4ID0gfkpQX1NhbGVzX1N1bSwgbmFtZSA9ICJKYXBhbiIpICU+JQ0KICBhZGRfdHJhY2UoeCA9IH5PdGhlcl9TYWxlc19TdW0sIG5hbWUgPSAiT3RoZXIiKSAlPiUNCiAgbGF5b3V0KA0KICAgIHRpdGxlID0gIlZpZGVvIEdhbWUgU2FsZXMgYnkgR2VucmUiLA0KICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJTYWxlcyAobWlsbGlvbikiKSwNCiAgICBiYXJtb2RlID0gImdyb3VwIg0KICApDQoNCmZpZw0KYGBgDQpIaWVyIHNpZWh0IG1hbiBndXQsIGRhc3MgSmFwYW4gaW0gVmVyZ2xlaWNoIHp1IGRlbiBhbmRlcmVuIE3DpHJrdGVuIHZpZWwgbWVociBSb2xlLVBsYXlpbmcgdW5kIFN0cmF0ZWdpZSBTcGllbGUgdmVya2F1ZnQuIERhZsO8ciB3ZXJkZW4gaW4gSmFwYW4gYWJlciB2aWVsIHdlbmlnZXIgU2hvb3RlciB1bmQgQWN0aW9uIFNwaWVsZSBhbHMgZGVuIGFuZGVyZW4gUmVnaW9uZW4gdmVya2F1ZnQuIEVVIHVuZCBOQSBhYmVyIMOkaG5saWNoZSBUcmVuZHMuDQoNCmBgYHtyfQ0KIyBDcmVhdGUgRGF0YWZyYW1lIHRvIHNob3cgdG90YWwgc2FsZXMgZm9yIGVhY2ggeWVhcg0KZGZfeWVhciA8LSBkZl9jbGVhbiAlPiUNCiAgZ3JvdXBfYnkoWWVhcikgJT4lDQogIHN1bW1hcml6ZSgNCiAgICBOQV9TYWxlc19TdW0gPSBzdW0oTkFfU2FsZXMpLA0KICAgIEVVX1NhbGVzX1N1bSA9IHN1bShFVV9TYWxlcyksIA0KICAgIEpQX1NhbGVzX1N1bSA9IHN1bShKUF9TYWxlcyksDQogICAgT3RoZXJfU2FsZXNfU3VtID0gc3VtKE90aGVyX1NhbGVzKSwNCiAgICBHbG9iYWxfU2FsZXNfU3VtID0gc3VtKEdsb2JhbF9TYWxlcykNCiAgKQ0KDQojIFBsb3Qgc3RhY2tlZCBiYXJjaGFydCBmcm9tIHRoZSBEYXRhRnJhbWUgYWJvdmUNCmZpZyA8LSBwbG90X2x5KA0KICBkZl95ZWFyLCB5ID0gfk5BX1NhbGVzX1N1bSwgeCA9IH5ZZWFyLCB0eXBlID0gImJhciIsIG5hbWUgPSAiTm9ydGggQW1lcmljYSIsIHdpZHRoID0gOTAwLCBoZWlnaHQgPSA1MDApICU+JSANCiAgYWRkX3RyYWNlKHkgPSB+RVVfU2FsZXNfU3VtLCBuYW1lID0gIkV1cm9wZSIpICU+JQ0KICBhZGRfdHJhY2UoeSA9IH5KUF9TYWxlc19TdW0sIG5hbWUgPSAiSmFwYW4iKSAlPiUNCiAgYWRkX3RyYWNlKHkgPSB+T3RoZXJfU2FsZXNfU3VtLCBuYW1lID0gIk90aGVyIikgJT4lDQogIGxheW91dCgNCiAgICB0aXRsZSA9ICJWaWRlbyBHYW1lIGZyb20gU2FsZXMgYnkgWWVhciIsDQogICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIlllYXIiKSwNCiAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiU2FsZXMgKG1pbGxpb24pIiksDQogICAgYmFybW9kZSA9ICJzdGFjayINCiAgKQ0KDQpmaWcNCmBgYA0Kw5xiZXJyYXNjaGVuZGVyd2Vpc2UsIHNpZWh0IG1hbiBpbiBkaWVzZW0gRGlhZ3JhbW0sIGRhc3MgZGllIFZpZGVvc3BpZWwgVmVya8OkdWZlIHp3aXNjaGVuIGRlbiBKYWhyZW4gMjAwNSB1bmQgMjAxMiBnZWJvb210IGhhYmVuLiBEYW5hY2ggaXN0IGRpZSBLdXJ2ZSB3aWVkZXIgYWJnZWZsYWNodC4gRGllcyBrw7ZubnRlIHVudGVyIGFuZGVyZW0gZGFyYW4gbGllZ2VuLCBkYXNzIGluIGRpZXNlciBaZWl0IHZpZWxlIG5ldWUgS29uc29sZW4gYXVmIGRlbiBNYXJrdCBnZWtvbW1lbiBzaW5kLCB3ZWxjaGUgZ3Jvc3NlIE5ldWVydW5nZW4gbWl0IHNpY2ggYnJhY2h0ZW4sIGRpZSBuaWNodCBpbiBkaWVzZW0gRGF0ZW5zYXR6IHNpbmQgKHpiIFBTNCkuIERpZSBQbGF5c3RhdGlvbiAzLCBkaWUgWGJveCAzNjAgdW5kIGRpZSBOaW50ZW5kbyBXaWkgc2luZCBhbGxlIGluIDIwMDUtMjAwNiBhdWYgZGVuIE1hcmt0IGdla29tbWVuIHVuZCBoYWJlbiBkZW4gU3BpZWxlbWFya3QgYW5zY2hlaW5lbmQgc3RhcmsgYmVlaW5mbHVzc3QuIA0KDQpgYGB7cn0NCiMgQ3JlYXRlIERhdGFmcmFtZSBhbmQgY2FsY3VsYXRlIHRoZSBhdmVyYWdlIG9mIHNhbGVzIGZvciBlYWNoIGdlbnJlIGZyb20gZWFjaCByZWdpb24NCmRmX3NhbGVzX2F2ZyA8LSBkZl9jbGVhbiAlPiUNCiAgZ3JvdXBfYnkoR2VucmUpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgRVVfU2FsZXNfQXZnID0gbWVhbihFVV9TYWxlcyksDQogICAgTkFfU2FsZXNfQXZnID0gbWVhbihOQV9TYWxlcyksDQogICAgSlBfU2FsZXNfQXZnID0gbWVhbihKUF9TYWxlcyksDQogICAgT3RoZXJfU2FsZXNfQXZnID0gbWVhbihPdGhlcl9TYWxlcyksDQogICAgR2xvYmFsX1NhbGVzX0F2ZyA9IG1lYW4oR2xvYmFsX1NhbGVzKSkNCg0KZGZfc2FsZXNfYXZnDQpgYGANCkhpZXIgc2VoZW4gd2lyIGRpZSBkdXJjaHNjaG5pdHRsaWNoZSBBbnpoYWwgdm9uIFZlcmvDpHVmZSBwcm8gR2VucmUuDQoNCiMjIFJlZ3Jlc3Npb25zbW9kZWxsZQ0KDQpEYSB3aXIgdW5zZXJlIERhdGVuIGpldHp0IGJlc3NlciB2ZXJzdGVoZW4sIGvDtm5uZW4gd2lyIG1pdCBkZW4gUmVncmVzc2lvbnNtb2RlbGxlbiB1bmQgbWl0IGRlciBCZWFudHdvcnR1bmcgdW5zZXJlciBGcmFnZXN0ZWxsdW5nIGJlZ2lubmVuLiBBbGxlcmRpbmdzIG11c3N0ZW4gd2lyIGZlc3RzdGVsbGVuLCBkYXNzIHVuc2VyZSBGcmFnZXN0ZWxsdW5nIHVuZ2VzY2hpY2t0IHdhciB1bmQgd2lyIGRpZXNlIG5pY2h0IGF1c3JlaWNoZW5kIGJlYW50d29ydGVuIGvDtm5uZW4uIERhaGVyIGhhYmVuIHdpciB1bnMgZm9sZ2VuZGUgQWx0ZXJuYXRpdi1GcmFnZXN0ZWxsdW5nIMO8YmVybGVndDoNCg0KIyMjIEthbm4gbWFuIGFuaGFuZCBkZXIgbm9yZGFtZXJpa2FuaXNjaGVuIFZlcmvDpHVmZSB2b3JhdXNzYWdlbiwgd2llIHNpY2ggZWluIEdlbnJlIGltIGV1cm9ww6Rpc2NoZW4gTWFya3QgdmVya2F1ZmVuIHdpcmQ/DQoNCkRpZXNlIEZyYWdlIGvDtm5udGUgZWluZXIgRmlybWEgZGFiZWkgaGVsZmVuLCB6dSBlbnRzY2hlaWRlbiwgd2lldmllbCBXZXJiZWJ1ZGdldCBkaWVzZSBpbiBFdXJvcGEgYXVzZ2ViZW4gc29sbCwgbmFjaGRlbSBlaW4gU3BpZWwgaW4gQW1lcmlrYSBiZXJlaXRzIGF1ZiBkZW4gTWFya3QgZ2Vrb21tZW4gaXN0LiANCg0KIyMjIERhdGVuIG9wdGltaWVyZW4NCg0KSW4gdW5zZXJlbSBEYXRlbnNhdHogZ2lidCBlcyBlaW5pZ2UgQXVzcmVpc3Nlci4gWnVtIEJlaXNwaWVsIGdpYnQgZXMgU3BpZWxlLCB3ZWxjaGUgbnVyIGluIGVpbnplbG5lbiBSZWdpb25lbiBhdWYgZGVuIE1hcmt0IGdla29tbWVuIHNpbmQgdW5kIGRpZSBpbiBhbmRlcmVuIEzDpG5kZXJuIGdhciBuaWNodCB2ZXJrYXVmdCB3dXJkZW4uIERpZXNlIFRhdHNhY2hlIHfDvHJkZSBiZWltIEVyc3RlbGxlbiBkZXIgTW9kZWxsZSB6dSBBYndlaWNodW5nZW4gdW5kIFVuZ2VuYXVpZ2tlaXRlbiBmw7xocmVuLiBEYWhlciBiZXLDvGNrc2ljaHRpZ2VuIHdpciBudXIgZGllIEdhbWVzLCB3ZWxjaGUgw7xiZXIgMSBNaW8uIFZlcmvDpHVmZSBoYWJlbi4NCg0KVW50ZW4gaGFiZW4gd2lyIGRpZSBHZW5yZXMgbWl0IGRlbiBiZXN0ZW4gRml0cyBnZW5vbW1lbiwgZGEgYmVpIGRlbiBhbmRlcmVuIFNwaWVsZ2VucmVzIGRlciBSLVNxdWFyZWQgV2VydCB1bnRlciA1MCUgbGFnLiBEYXMgYmVkZXV0ZXQsIGRhc3MgZXMgZG9ydCBrZWluZW4gWnVzYW1tZW5oYW5nIGdpYnQsIHdlc2hhbGIgd2lyIGRpZXNlIGViZW5mYWxscyBuaWNodCBiZWFjaHRlbiB3ZXJkZW4uDQoNCmBgYHtyfQ0KIyBDcmVhdGUgRGF0YUZyYW1lIG9ubHkgd2l0aCBSYWNpbmcgZ2FtZXMNCmRmX3JhY2luZyA8LSBkZl9jbGVhbiAlPiUNCiAgZmlsdGVyKA0KICAgIEdlbnJlID09ICJSYWNpbmciLA0KICAgIE5BX1NhbGVzID4gMS4wMCwNCiAgICBFVV9TYWxlcyA+IDEuMDANCiAgKQ0KDQojIENyZWF0ZSBzY2F0dGVycGxvdA0KZ2dwbG90KGRmX3JhY2luZywgYWVzKHg9RVVfU2FsZXMsIHk9TkFfU2FsZXMpKSArIA0KICBnZW9tX3BvaW50KCkgKyANCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UpICsNCiAgbGFicyh0aXRsZSA9ICJSYWNpbmcgR2FtZSBTYWxlcyBPdmVydmlldyIpDQpgYGAgDQpgYGB7cn0NCiMgQ3JlYXRlIGxpbmVhciBtb2RlbA0KbWRsX3JhY2luZyA8LSBsbShFVV9TYWxlcyB+IE5BX1NhbGVzLCBkYXRhID0gZGZfcmFjaW5nKQ0KDQojIEV4dHJhY3QgbW9kZWwgc2NvcmUNCm1kbF9yYWNpbmcgJT4lDQogIGdsYW5jZSgpICU+JQ0KICBwdWxsKHIuc3F1YXJlZCkNCmBgYA0KUi5TcXVhcmVkIGlzdCBzb3p1c2FnZW4gZGllIEdlbmF1aWdrZWl0IGRlcyBNb2RlbGxzLiBEaWVzZXMgTW9kZWxsIHZvbiBSYWNpbmcgR2FtZXMgaW4gZGVyIEVVIHVuZCBpbiBOQSBsaWVndCBiZWkgZWluZXIgYWt6ZXB0YWJsZW4gV2VydCB2b24gODYlDQoNCmBgYHtyfQ0KIyBQcmVkaWN0IEVVIFNhbGVzIGZvciBhIFJhY2luZyBHYW1lIGJhc2VkIG9uIE5BIFNhbGVzDQpwcmVkaWN0X3JhY2luZyA8LSB0aWJibGUoTkFfU2FsZXMgPSA1KQ0KcHJlZGljdChtZGxfcmFjaW5nLCBwcmVkaWN0X3JhY2luZykNCmBgYA0KIyMjIyBWb3JoZXJzYWdlDQpXZW5uIGVpbiBSYWNpbmcgR2FtZSBpbiBOb3JkIEFtZXJpa2EgNSBNaWxsaW9uZW4gVmVya8OkdWZlIGF1ZndlaXN0LCBsaWVndCBkaWUgVmVya2F1ZnMtVm9yaGVyc2FnZSBmw7xyIEV1cm9wYSBiZWkgcnVuZCAzLjkgTWlsbGlvbmVuLiBJbiBBbWVyaWthIHNpbmQgZGllc2UgU3BpZWxlIGFsc28gYmVsaWVidGVyIGFscyBiZWkgdW5zIGluIEV1cm9wYS4NCg0KYGBge3J9DQojIENyZWF0ZSBEYXRhRnJhbWUgb25seSB3aXRoIFJvbGUtUGxheWluZyBnYW1lcw0KZGZfcm9sZSA8LSBkZl9jbGVhbiAlPiUNCiAgZmlsdGVyKA0KICAgIEdlbnJlID09ICJSb2xlLVBsYXlpbmciLA0KICAgIE5BX1NhbGVzID4gMS4wMCwNCiAgICBFVV9TYWxlcyA+IDEuMDANCiAgKQ0KDQojIENyZWF0ZSBzY2F0dGVycGxvdA0KZ2dwbG90KGRmX3JvbGUsIGFlcyh4PUVVX1NhbGVzLCB5PU5BX1NhbGVzKSkgKyANCiAgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFKSArDQogIGxhYnModGl0bGUgPSAiUm9sZS1QbGF5aW5nIEdhbWUgU2FsZXMgT3ZlcnZpZXciKQ0KYGBgDQpgYGB7cn0NCiMgQ3JlYXRlIGxpbmVhciBtb2RlbA0KbWRsX3JwIDwtIGxtKEVVX1NhbGVzIH4gTkFfU2FsZXMsIGRhdGEgPSBkZl9yb2xlKQ0KDQojIEV4dHJhY3QgbW9kZWwgc2NvcmUNCm1kbF9ycCAlPiUNCiAgZ2xhbmNlKCkgJT4lDQogIHB1bGwoci5zcXVhcmVkKQ0KYGBgDQpEYXMgTW9kZWxsIHZvbiBSb2xlLVBsYXlpbmcgU3BpZWxlIGluIE5BIHVuZCBFVSBiZXRyw6RndCBhdWNoIGVpbiBha3plcHRhYmxlciBXZXJ0IHZvbiA4NCUNCg0KYGBge3J9DQojIFByZWRpY3QgRVUgU2FsZXMgZm9yIGEgUm9sZS1QbGF5aW5nIEdhbWUgYmFzZWQgb24gTkEgU2FsZXMNCnByZWRpY3RfcnAgPC0gdGliYmxlKE5BX1NhbGVzID0gNSkNCnByZWRpY3QobWRsX3JwLCBwcmVkaWN0X3JwKQ0KYGBgDQpgYGB7cn0NCiMgQ3JlYXRlIERhdGFGcmFtZSBvbmx5IHdpdGggU2ltdWxhdGlvbiBnYW1lcw0KZGZfc2ltIDwtIGRmX2NsZWFuICU+JQ0KICBmaWx0ZXIoDQogICAgR2VucmUgPT0gIlNpbXVsYXRpb24iLA0KICAgIE5BX1NhbGVzID4gMS4wMCwNCiAgICBFVV9TYWxlcyA+IDEuMDANCiAgKQ0KDQojIENyZWF0ZSBzY2F0dGVycGxvdA0KZ2dwbG90KGRmX3NpbSwgYWVzKHg9RVVfU2FsZXMsIHk9TkFfU2FsZXMpKSArIA0KICBnZW9tX3BvaW50KCkgKyANCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UpICsNCiAgbGFicyh0aXRsZSA9ICJTaW11bGF0aW9uIEdhbWUgU2FsZXMgT3ZlcnZpZXciKQ0KYGBgDQpgYGB7cn0NCiMgQ3JlYXRlIGxpbmVhciBtb2RlbA0KbWRsX3NpbSA8LSBsbShFVV9TYWxlcyB+IE5BX1NhbGVzLCBkYXRhID0gZGZfc2ltKQ0KDQojIEV4dHJhY3QgbW9kZWwgc2NvcmUNCm1kbF9zaW0gJT4lDQogIGdsYW5jZSgpICU+JQ0KICBwdWxsKHIuc3F1YXJlZCkNCmBgYA0KRGFzIE1vZGVsbCB2b24gU2ltdWxhdGlvbiBTcGllbGUgaW4gTkEgdW5kIEVVIGJldHLDpGd0IGF1Y2ggZWluIGdlbmF1ZXIgV2VydCB2b24gOTIlDQoNCmBgYHtyfQ0KIyBQcmVkaWN0IEVVIFNhbGVzIGZvciBhIFNwb3J0IEdhbWUgYmFzZWQgb24gTkEgU2FsZXMNCnByZWRpY3Rfc2ltIDwtIHRpYmJsZShOQV9TYWxlcyA9IDUpDQpwcmVkaWN0KG1kbF9zaW0sIHByZWRpY3Rfc2ltKQ0KYGBgDQoNCiMjIyMgVm9yaGVyc2FnZQ0KV2VubiBlaW4gU2ltdWxhdGlvbiBHYW1lIGluIE5BIDUgTWlsbGlvbiBWZXJrw6R1ZmUgYXVmd2Vpc3QsIGxpZWd0IGRpZSBWb3JoZXJzYWdlIGbDvHIgRVUgYmVpIDUuNCBNaWxsaW9uZW4uIFVuZCBTb21pdCBpc3QgZGllIEdlbnJlIFNpbXVsYXRpb24gZGllIGVyc3RlLCBiZWkgZGVyIGRpZSBWb3JoZXJzYWdlbiBkZXIgVmVya8OkdWZlIGdyw7Zzc2VyIHNpbmQgYWxzIGluIE5vcmQgQW1lcmlrYS4NCg0KYGBge3J9DQojIENyZWF0ZSBEYXRhRnJhbWUgb25seSB3aXRoIFNwb3J0IGdhbWVzDQpkZl9zcG9ydCA8LSBkZl9jbGVhbiAlPiUNCiAgZmlsdGVyKA0KICAgIEdlbnJlID09ICJTcG9ydHMiLA0KICAgIE5BX1NhbGVzID4gMS4wMCwNCiAgICBFVV9TYWxlcyA+IDEuMDANCiAgKQ0KDQojIENyZWF0ZSBzY2F0dGVycGxvdA0KZ2dwbG90KGRmX3Nwb3J0LCBhZXMoeD1FVV9TYWxlcywgeT1OQV9TYWxlcykpICsgDQogIGdlb21fcG9pbnQoKSArIA0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSkgKw0KICBsYWJzKHRpdGxlID0gIlNwb3J0IEdhbWUgU2FsZXMgT3ZlcnZpZXciKQ0KYGBgDQpIaWVyIGhhYmVuIHdpciB3aWVkZXIgZWluIFNwaWVsLCB3ZWxjaGVzIHNpY2ggdm9uIGRlbiBNYXNzZW4gYWJoZWJ0LCBhYmVyIGluIEFtZXJpa2Egc293aWUgaW4gRXVyb3BhIGV4dHJlbSBiZWxpZWJ0IHdhci4gRXMgaGFuZGVsdCBzaWNoIG5hdMO8cmxpY2ggdW0gZGFzIEt1bHRzcGllbCAiV2lpIFNwb3J0cyIuIERpZXNlcyB3dXJkZSBlaW5lbSBhdWNoIG1laXN0ZW5zIGJlaW0gS2F1ZiBlaW5lciBOaW50ZW5kbyBXaWkgS29uc29sZSBkYXp1IGdlc2NoZW5rdC4gDQoNCmBgYHtyfQ0KIyBDcmVhdGUgbGluZWFyIG1vZGVsDQptZGxfc3BvcnQgPC0gbG0oRVVfU2FsZXMgfiBOQV9TYWxlcywgZGF0YSA9IGRmX3Nwb3J0KQ0KDQojIEV4dHJhY3QgbW9kZWwgc2NvcmUNCm1kbF9zcG9ydCAlPiUNCiAgZ2xhbmNlKCkgJT4lDQogIHB1bGwoci5zcXVhcmVkKQ0KYGBgDQpEYXMgTW9kZWxsIHZvbiBTcG9ydCBTcGllbGUgaW4gTkEgdW5kIEVVIGlzdCBzZWhyIGdlbmF1IGJlaSBrbmFwcCA5NSUuIERhZHVyY2ggc2luZCBkaWUgVm9yaGVyc2FnZW4gZWJlbmZhbGxzIHNlaHIgZ2VuYXUuDQoNCmBgYHtyfQ0KIyBQcmVkaWN0IEVVIFNhbGVzIGZvciBhIFNwb3J0IEdhbWUgYmFzZWQgb24gTkEgU2FsZXMNCnByZWRpY3Rfc3BvcnQgPC0gdGliYmxlKE5BX1NhbGVzID0gNSkNCnByZWRpY3QobWRsX3Nwb3J0LCBwcmVkaWN0X3Nwb3J0KQ0KYGBgDQojIyMjIFZvcmhlcnNhZ2UNCldlbm4gZWluIFNwb3J0IEdhbWUgaW4gTkEgNSBNaWxsaW9uIFZlcmvDpHVmZSBhdWZ3ZWlzdCwgbGllZ3QgZGllIFZvcmhlcnNhZ2UgZsO8ciBFVSBiZWkgNCBNaWxsaW9uZW4uDQoNCg0KIyMjIFJlc2lkdWVuYW5hbHlzZSAoenVtIGJldXJ0ZWlsZW4gb2IgZGFzIE1vZGVsbCBndXQgaXN0KQ0KRGllIFJlc2lkdWVuIHNvbGx0ZW4gZm9sZ2VuZGUgUHVua3RlIGVyZsO8bGxlbjoNCg0KLSBSZXNpZHVlbiBoYWJlbiBkZW4gZXJ3YXJ0dW5nc3dlcnQgdm9uIDANCi0gUmVzaWR1ZW4gc2luZCB2b25laW5hbmRlciB1bmFiaMOkbmdpZw0KLSBSZXNpZHVlbiBzaW5kIG5vcm1hbHZlcnRlaWx0DQoNCkRpZXMgbcO8c3NlbiB3aXIgbnVuIG5vY2ggcHLDvGZlbiwgdW0genUgYmVzdGltbWVuLCBvYiB1bnNlcmUgVm9yaGVyc2FnZW4gdmVybMOkc3NsaWNoIHNpbmQuIA0KDQpgYGB7cn0NCiMgQ3JlYXRlIFJlc2lkdWFsIFNjYXR0ZXJwbG90DQoNCmRmIDwtIGF1Z21lbnQobWRsX3JhY2luZykNCg0KIyBwbG90IHJlc2lkdWFscw0KZ2dwbG90KGRmLCBhZXMoeCA9IDE6bnJvdyhkZiksIHkgPSAucmVzaWQpKSArIA0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCwgY29sb3I9IlJlZCIpICsNCiAgZ2d0aXRsZSgiUmVzaWR1YWxzIE1vZGVsIFJhY2luZyBHZW5yZSIpICsNCiAgeGxhYigiIikNCmBgYA0KYGBge3J9DQojIENyZWF0ZSBSZXNpZHVhbCBIaXN0b2dyYW0gKHRvIHNlZSBpZiB0aGUgZGF0YSBpcyBhIG5vcm1hbCBkaXN0cmlidXRpb24pDQoNCmdncGxvdChkZl9yYWNpbmcsIGFlcyh4ID0gbWRsX3JhY2luZyRyZXNpZHVhbHMpKSArIA0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzApICsNCiAgZ2VvbV9kZW5zaXR5KGNvbG9yID0gIlJlZCIpICsNCiAgZ2d0aXRsZSgiUmVzaWR1YWxzIE1vZGVsIFJhY2luZyBHZW5yZSIpICsNCiAgeGxhYigicmVzaWR1YWxzIikNCmBgYA0KTWl0IEhpbGZlIGRlciByb3RlbiBMaW5pZSBzaWVodCBtYW4sIGRhc3MgZGFzIEhpc3RvZ3JhbW0gIG5pY2h0IDEwMCUgbm9ybWFsdmVydGVpbHQgaXN0LiBFcyBrb21tdCBqZWRvY2ggc2Nob24gbmFoZSBhbiBlaWUgTm9ybWFsdmVydGVpbHVuZyBoZXJhbiwgd2VzaGFsYiB3aXIgZGFzIHNvIGFremVwdGllcmVuIGvDtm5uZW4uDQoNCmBgYHtyfQ0KIyBDcmVhdGUgUmVzaWR1YWwgU2NhdHRlcnBsb3QNCg0KZGYgPC0gYXVnbWVudChtZGxfcnApDQoNCmdncGxvdChkZiwgYWVzKHggPSAxOm5yb3coZGYpLCB5ID0gLnJlc2lkKSkgKyANCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTAsIGNvbG9yPSJSZWQiKSArDQogIGdndGl0bGUoIlJlc2lkdWFscyBNb2RlbCBSb2xlLVBsYXlpbmcgR2VucmUiKSArDQogIHhsYWIoIiIpDQpgYGANCmBgYHtyfQ0KIyBDcmVhdGUgUmVzaWR1YWwgSGlzdG9ncmFtICh0byBzZWUgaWYgdGhlIGRhdGEgaXMgYSBub3JtYWwgZGlzdHJpYnV0aW9uKQ0KDQpnZ3Bsb3QoZGZfcm9sZSwgYWVzKHggPSBtZGxfcnAkcmVzaWR1YWxzKSkgKyANCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDMwKSArDQogIGdlb21fZGVuc2l0eShjb2xvciA9ICJSZWQiKSArDQogIGdndGl0bGUoIlJlc2lkdWFscyBNb2RlbCBSb2xlLVBsYXlpbmcgR2VucmUiKSArDQogIHhsYWIoInJlc2lkdWFscyIpDQpgYGANCkF1Y2ggc2llaHQgbWFuLCBkYXNzIGRhcyBIaXN0b2dyYW1tICBuaWNodCAxMDAlIG5vcm1hbHZlcnRlaWx0IGlzdC4gRXMga29tbXQgamVkb2NoIHNjaG9uIG5haGUgYW4gZWllIE5vcm1hbHZlcnRlaWx1bmcgaGVyYW4sIHdlc2hhbGIgd2lyIGRhcyBzbyBha3plcHRpZXJlbiBrw7ZubmVuLg0KYGBge3J9DQojIENyZWF0ZSBSZXNpZHVhbCBTY2F0dGVycGxvdA0KDQpkZiA8LSBhdWdtZW50KG1kbF9zaW0pDQoNCmdncGxvdChkZiwgYWVzKHggPSAxOm5yb3coZGYpLCB5ID0gLnJlc2lkKSkgKyANCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTAsIGNvbG9yPSJSZWQiKSArDQogIGdndGl0bGUoIlJlc2lkdWFscyBNb2RlbCBTaW11bGF0aW9uIEdlbnJlIikgKw0KICB4bGFiKCIiKQ0KYGBgDQpgYGB7cn0NCiMgQ3JlYXRlIFJlc2lkdWFsIEhpc3RvZ3JhbSAodG8gc2VlIGlmIHRoZSBkYXRhIGlzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbikNCg0KZ2dwbG90KGRmX3NpbSwgYWVzKHggPSBtZGxfc2ltJHJlc2lkdWFscykpICsgDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAzMCkgKw0KICBnZW9tX2RlbnNpdHkoY29sb3IgPSAiUmVkIikgKw0KICBnZ3RpdGxlKCJSZXNpZHVhbHMgTW9kZWwgU2ltdWxhdGlvbiBHZW5yZSIpICsNCiAgeGxhYigicmVzaWR1YWxzIikNCmBgYA0KTWl0IEhpbGZlIGRlciByb3RlbiBMaW5pZSBzaWVodCBtYW4sIGRhc3MgZGFzIEhpc3RvZ3JhbW0gIG5pY2h0IDEwMCUgbm9ybWFsdmVydGVpbHQgaXN0LiBFcyBrb21tdCBqZWRvY2ggc2Nob24gbmFoZSBhbiBlaWUgTm9ybWFsdmVydGVpbHVuZyBoZXJhbiwgd2VzaGFsYiB3aXIgZGFzIHNvIGFremVwdGllcmVuIGvDtm5uZW4uDQoNCmBgYHtyfQ0KIyBDcmVhdGUgUmVzaWR1YWwgU2NhdHRlcnBsb3QNCg0KZGYgPC0gYXVnbWVudChtZGxfc3BvcnQpDQoNCmdncGxvdChkZiwgYWVzKHggPSAxOm5yb3coZGYpLCB5ID0gLnJlc2lkKSkgKyANCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTAsIGNvbG9yPSJSZWQiKSArDQogIGdndGl0bGUoIlJlc2lkdWFscyBNb2RlbCBTcG9ydCBHZW5yZSIpICsNCiAgeGxhYigiIikNCmBgYA0KYGBge3J9DQojIENyZWF0ZSBSZXNpZHVhbCBIaXN0b2dyYW0gKHRvIHNlZSBpZiB0aGUgZGF0YSBpcyBhIG5vcm1hbCBkaXN0cmlidXRpb24pDQoNCmdncGxvdChkZl9zcG9ydCwgYWVzKHggPSBtZGxfc3BvcnQkcmVzaWR1YWxzKSkgKyANCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDUwKSArDQogIGdlb21fZGVuc2l0eShjb2xvciA9ICJSZWQiKSArDQogIGdndGl0bGUoIlJlc2lkdWFscyBNb2RlbCBTcG9ydCBHZW5yZSIpICsNCiAgeGxhYigicmVzaWR1YWxzIikNCmBgYA0KTWl0IEhpbGZlIGRlciByb3RlbiBMaW5pZSBzaWVodCBtYW4sIGRhc3MgZGFzIEhpc3RvZ3JhbW0gIG5pY2h0IDEwMCUgbm9ybWFsdmVydGVpbHQgaXN0LiBFcyBrb21tdCBqZWRvY2ggc2Nob24gbmFoZSBhbiBlaWUgTm9ybWFsdmVydGVpbHVuZyBoZXJhbiwgd2VzaGFsYiB3aXIgZGFzIHNvIGFremVwdGllcmVuIGvDtm5uZW4uDQoNCiMjIyBXaWUgw6RobmxpY2ggc2luZCBzaWNoIGRpZSBWZXJrYXVmc3phaGxlbiB2b24gTm9yZGFtZXJpa2EgdW5kIEV1cm9wYT8gVW5kIHdpZSB2ZXJoYWx0ZW4gc2ljaCBkaWUgamFwYW5pc2NoZW4gVmVya2F1ZnN6YWhsZW4gaW0gVmVyZ2xlaWNoIHp1IGRlbiB6d2VpIHdlc3RsaWNoZW4gQWJzYXR6bcOkcmt0ZW4/DQoNCmBgYHtyfQ0KIyBDb2xvciB0aGUgU1BMT00gb2YgTkFfU2FsZXMsIEVVX1NhbGVzLCBhbmQgSlBfU2FsZXMgYnkgbmludGVuZG8NCmRmX2NsZWFuICU+JQ0KICBwbG90X2x5KGNvbG9yID0gfkdlbnJlKSAlPiUgDQogIGFkZF90cmFjZSgNCiAgICB0eXBlID0gJ3NwbG9tJywNCiAgICBkaW1lbnNpb25zID0gbGlzdCgNCiAgICAgIGxpc3QobGFiZWwgPSAnTi4gQW1lcmljYScsIHZhbHVlcyA9IH5OQV9TYWxlcyksDQogICAgICBsaXN0KGxhYmVsID0gJ0V1cm9wZScsIHZhbHVlcyA9IH5FVV9TYWxlcyksICAgIA0KICAgICAgbGlzdChsYWJlbCA9ICdKYXBhbicsIHZhbHVlcyA9IH5KUF9TYWxlcykgICAgICAgDQogICAgKQ0KICApDQpgYGANCsOEdXNzZXJzdCBzcGFubmVuZCB6dSBzZWhlbiBpc3QsIGRhc3Mgc2ljaCBncm9zc2UgVW50ZXJzY2hpZWRlIHNlaGVuIGxhc3Nlbiwgd2llIHNpY2ggYmVzdGltbXRlIEdlbnJlIHZlcmthdWZ0IGhhYmVuLiBEaWUgZ2VvZ3JhcGhpc2NoZSBMYWdlIHZvbiBkZW0gQWJzYXR6bWFya3QgaXN0IGRhYmVpIGVoZXIgendlaXRyYW5naWcuIE1pdCBBYnN0YW5kIGFtIGjDpHVmaWdzdGVuIHd1cmRlbiBBY3Rpb24gU3BpZWxlIHZlcmthdWZ0LiANCg0KDQojIyMgVmVya2F1ZmVuIHNpY2ggTmludGVuZG8gU3BpZWxlIHdlbHR3ZWl0IGJlc3NlciBhbHMgU3BpZWxlIEVsZWN0cm9uaWMgQXJ0cy4gDQoNCkRhZsO8ciBtw7xzc2VuIHdpciB6dWVyc3QgdW5zZXIgRGF0YWZyYW1lIEZpbHRlcm4sIGRhbWl0IHdpciBudXJub2NoIFNwaWVsZSB2b24gTmludGVuZG8gdW5kIEVsZWN0cm9uaWMgQXJ0cyBoYWJlbi4NCmBgYHtyfQ0KIyBDcmVhdGUgRGF0YUZyYW1lIHdpdGggb25seSBOaW50ZW5kbyBhbmQgU29ueSBhcyBQdWJsaXNoZXINCnB1Ymxpc2hlcnMgPSBjKCJOaW50ZW5kbyIsICJFbGVjdHJvbmljIEFydHMiKQ0KDQpkZl9wdWJsaXNoZXIgPC0gZGZfY2xlYW4gJT4lDQogIGZpbHRlcigNCiAgICBQdWJsaXNoZXIgJWluJSBwdWJsaXNoZXJzDQogICAgKQ0KDQpkZl9wdWJsaXNoZXINCmBgYA0KYGBge3J9DQojIFJlcGxhY2UgcHVibGlzaGVyIG5hbWUgd2l0aCAwIGFuZCAxDQpkZl9wdWJsaXNoZXIkUHVibGlzaGVyW2RmX3B1Ymxpc2hlciRQdWJsaXNoZXIgPT0gIkVsZWN0cm9uaWMgQXJ0cyJdIDwtIDANCmRmX3B1Ymxpc2hlciRQdWJsaXNoZXJbZGZfcHVibGlzaGVyJFB1Ymxpc2hlciA9PSAiTmludGVuZG8iXSA8LSAxDQoNCiMgU2F2ZSBhcyBpbnQNCmRmX3B1Ymxpc2hlciRQdWJsaXNoZXIgPC0gYXMubnVtZXJpYyhkZl9wdWJsaXNoZXIkUHVibGlzaGVyKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDcmVhdGUgbG9naXN0aWMgbW9kZWwNCm1kbF9wdWJsaXNoZXIgPC0gZ2xtKFB1Ymxpc2hlciB+IE5BX1NhbGVzLCBkYXRhID0gZGZfcHVibGlzaGVyLCBmYW1pbHkgPSBiaW5vbWlhbCgpKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRmX3B1Ymxpc2hlciwgYWVzKHg9R2xvYmFsX1NhbGVzLCB5PVB1Ymxpc2hlcikpICsgDQogIGdlb21fcG9pbnQoYWxwaGE9LjUsIGNvbG9yPSJCbHVlIikgKw0KICBzdGF0X3Ntb290aChtZXRob2Q9ImdsbSIsIGNvbCA9ICJSZWQiLCBzZT1GQUxTRSwgbWV0aG9kLmFyZ3MgPSBsaXN0KGZhbWlseT1iaW5vbWlhbCkpICsNCiAgbGFicygNCiAgICB4ID0gIlNhbGVzIChtaWxsaW9uKSIsDQogICAgeSA9ICIxPU5pbnRlbmRvIC8gMD1FbGVjdHJvbmljIEFydHMiLA0KICAgIHRpdGxlID0gIlByb2JhYmlsaXR5IHRoYXQgYSBnYW1lIGlzIGZyb20gTmludGVuZG8gYmFzZWQgb24gZ2xvYmFsIHNhbGVzIg0KICApDQpgYGANCldpciBrw7ZubmVuIGhpZXIgZ3V0IHNlaGVuLCBkYXNzIE5pbnRlbmRvIHZpZWwgZXJmb2xncmVpY2hlcmUgU3BpZWxlIHByb2R1emllcnQgaGF0LiBEaWVzIGRldXRldCBhdWNoIGFuLCBkYXNzIE5pbnRlbmRvIGJlbGllYnRlciBpc3QgYWxzIEVsZWN0cm9uaWMgQXJ0cy4gDQoNCiMjIyBWZXJrYXVmZW4gc2ljaCBpbiBKYXBhbiBOaW50ZW5kbyBXaWkgU3BpZWxlIGJlc3NlciBhbHMgTmludGVuZG8gRFMgU3BpZWxlPw0KDQpOdW4gZmlsdGVybiB3aXIgenVlcnN0IHVuc2VyZW4gRGF0YWZyYW1lIG5hY2ggV2lpIHVuZCBuYWNoIERTIFNwaWVsZW4uDQoNCmBgYHtyfQ0KIyBDcmVhdGUgRGF0YUZyYW1lIHdpdGggb25seSBXaWkgYW5kIERTIGFzIFBsYXRmb3Jtcw0KcGxhdGZvcm1zID0gYygiV2lpIiwgIkRTIikNCg0KZGZfcGxhdGZvcm0gPC0gZGZfY2xlYW4gJT4lDQogIGZpbHRlcigNCiAgICBQbGF0Zm9ybSAlaW4lIHBsYXRmb3Jtcw0KICAgICkNCg0KZGZfcGxhdGZvcm0NCmBgYA0KYGBge3J9DQojIFJlcGxhY2UgcGxhdGZvcm0gbmFtZSB3aXRoIDAgYW5kIDENCmRmX3BsYXRmb3JtJFBsYXRmb3JtW2RmX3BsYXRmb3JtJFBsYXRmb3JtID09ICJXaWkiXSA8LSAwDQpkZl9wbGF0Zm9ybSRQbGF0Zm9ybVtkZl9wbGF0Zm9ybSRQbGF0Zm9ybSA9PSAiRFMiXSA8LSAxDQoNCiMgU2F2ZSBhcyBpbnQNCmRmX3BsYXRmb3JtJFBsYXRmb3JtIDwtIGFzLm51bWVyaWMoZGZfcGxhdGZvcm0kUGxhdGZvcm0pDQpgYGANCg0KYGBge3J9DQojIENyZWF0ZSBsb2dpc3RpYyBtb2RlbA0KbWRsX3BsYXRmb3JtIDwtIGdsbShQbGF0Zm9ybSB+IEpQX1NhbGVzLCBkYXRhID0gZGZfcGxhdGZvcm0sIGZhbWlseSA9IGJpbm9taWFsKCkpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGZfcGxhdGZvcm0sIGFlcyh4PUpQX1NhbGVzLCB5PVBsYXRmb3JtKSkgKyANCiAgZ2VvbV9wb2ludChhbHBoYT0uMiwgY29sb3I9IkJsdWUiKSArDQogIHN0YXRfc21vb3RoKG1ldGhvZD0iZ2xtIiwgY29sID0gIlJlZCIsIHNlPUZBTFNFLCBtZXRob2QuYXJncyA9IGxpc3QoZmFtaWx5PWJpbm9taWFsKSkgKw0KICBsYWJzKA0KICAgIHggPSAiU2FsZXMgKG1pbGxpb24pIiwNCiAgICB5ID0gIjE9V2lpIC8gMD1EUyIsDQogICAgdGl0bGUgPSAiUHJvYmFiaWxpdHkgdGhhdCBhIGdhbWUgaXMgZnJvbSBXaWkgYmFzZWQgb24gaXQncyBzYWxlcyBpbiBKYXBhbiINCiAgKQ0KYGBgDQpBbmhhbmQgdm9uIGRpZXNlbSBQbG90IGthbm4gbWFuIGVya2VubmVuLCBkYXNzIGVpbiBTcGllbCwgd2VsY2hlcyBzaWNoIMO8YmVyIDIgTWlvLiBtYWwgdmVya2F1ZnQgaGF0LCBlaGVyIMO8YmVyIGRpZSBQbGF0Zm9ybSBXaWkgdmVya2F1ZnQgd3VyZGUuIERhcmF1cyBrw7ZubnRlIG1hbiBkZXV0ZW4sIGRhc3MgZGllIE5pbnRlbmRvIFdpaSBpbiBKYXBhbiBiZWxpZWJ0ZXIgaXN0IGFscyBkZXIgTmludGVuZG8gRFMuDQoNCiMjIyBGYXppdA0KQmVpIGRlbiBtZWlzdGVuIEdlbnJlbiBpc3QgZXMgbmljaHQgbcO2Z2xpY2ggZGllIFZlcmvDpHVmZSBpbiBFdXJvcGEgYW5oYW5kIGRlciBWZXJrw6R1ZmUgaW4gTm9yZCBBbWVyaWthIHZvcmhlcnp1c2FnZW4uIFdpciBoYWJlbiBqZWRvY2ggZWluaWdlIEdlbnJlbiBnZWZ1bmRlbiwgYmVpIGRlbmVuIFZvcmhlcnNhZ2VuIG3DtmdsaWNoIGlzdDogDQoNCi0gUmFjaW5nIC8gUmVubnNwaWVsZQ0KLSBSb2xlLVBsYXlpbmcgLyBSb2xsZW5zcGllbGUNCi0gU2ltdWxhdGlvbiAvIFNpbXVsYXRpb25zc3BpZWxlDQotIFNwb3J0IC8gU3BvcnRzcGllbGUNCg0KU2VociBpbnRlcmVzc2FudCB6dSBzZWhlbiB3YXIgZGFzIEdlbnJlIFNpbXVsYXRpb24uIFVuc2VyIE1vZGVsbCwgd2VsY2hlcyBiZWkgOTIlIEdlbmF1aWdrZWl0IGxpZWd0IHNhZ3Qgdm9yYXVzLCBkYXNzIGVpbiBiZWxpZWJpZ2VzIFNpbXVsYXRpb24gU3BpZWwgaW4gRXVyb3BhIGJlc3NlciB2ZXJrYXVmdCB3aXJkIGFscyBpbiBOb3JkIEFtZXJpa2EuIEJlaSBhbGxlbiBhbmRlcmVuIEdlbnJlbiB2ZXJrYXVmZW4gc2ljaCBkaWUgU3BpZWxlIGluIE5vcmQgQW1lcmlrYSBiZXNzZXIu